// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose:   Implementation of the methods to set psuedoclasses.
//   $Id$


#include "stdafx.h"
#include "XFormsElement.h"
#include "dispex.h"
#include "ExecuteDispatch.h"
#include "XFormsXFormsElement.h"
#include "ActivateBodyElement.h"
#include "CSSEffects.h"
// <summary>replaces an occurrence of sReplacing with sSetting in the className property.  If no occurrence of sReplacing is found, concatenates sSetting to the className</summary>
HRESULT CXFormsElement::SetClassProperty(IHTMLElement * pEl, BSTR  bsSetting, BSTR  bsReplacing)
{
	ASSERT(pEl != 0);
	HRESULT hr = E_FAIL;
	//fish out the className property, a space separated list of classes
	CComBSTR bsClasses;
	pEl->get_className(&bsClasses);
	
	//replace the unsetting class with the setting class
	CStringW sClasses(bsClasses);

	bool bReplaced;
	if(!(bReplaced = (sClasses.Replace(bsReplacing,bsSetting) > 0)) && sClasses.Find(bsSetting) == -1)
	{
		//if no replace occured (ie. unsetting class was not there)
		//	append the setting class.
		bReplaced = true;
		sClasses += bsSetting;
	}

	if(bReplaced) {
		bsClasses.Attach(sClasses.AllocSysString());
		hr =  pEl->put_className(bsClasses);
		m_bUpdatePending = true;
	}
	


	//start at the space.
	if(m_pXForms && m_pXForms->m_XFConfig.FormBrandingDebugInfo() && m_sDebugInfo.Replace(&(bsReplacing)[1],&(bsSetting)[1]))
	{
		put_XPathExpression();
	}
	return hr;
}

HRESULT CXFormsElement::SetClassPropertyOnViewlinkBody(BSTR bsSetting, BSTR bsReplacing)
{
	CComPtr<IHTMLDocument> pDoc;
	HRESULT hr = GetViewlinkDoc(&pDoc);
	CComQIPtr<IHTMLDocument2> pDoc2(pDoc);
	if(pDoc2)
	{
		CComPtr<IHTMLElement> pBodEl;
		hr = pDoc2->get_body(&pBodEl);
		if(pBodEl)
		{
			SetClassProperty(pBodEl,bsSetting,bsReplacing);
			CComPtr<IHTMLStyle> pStyle;
			pBodEl->get_style(&pStyle);
			if(pStyle)
			{
				pStyle->put_border(L"0px transparent solid");
				pStyle->put_display(L"inline");
				pStyle->put_visibility(L"visible");
				pStyle->put_backgroundColor(CComVariant(L"transparent"));
			}
		}
	}
	return hr;
}

// <summary>applies readonly/readwrite style</summary>
HRESULT CXFormsElement::readonly(BOOL bReadonly)
{
	HRESULT hr = E_FAIL;
	//Get the correct element to attach the class to.
	// Don't need to add the css class to the element as we would already 
	// have done this by calling IBoundTarget::setInitalState() in CBoundNode::addTarget()
	if(!m_bBindingToTarget) {
		CComQIPtr<IHTMLElement> pEl;
		if(m_pMain)
		{
			pEl = m_pMain;
		}
		else
		{
			GetMasterElement(&pEl);
		}
		if(pEl)
		{
			//get the properties, the right way around.
			const BSTR * pbsSetting = 0;
			const BSTR * pbsUnsetting = 0;

			if(bReadonly)
			{
				pbsSetting = &cssClassReadOnly;
				pbsUnsetting = &cssClassReadWrite;
			}	
			else
			{
				pbsUnsetting = &cssClassReadOnly;
				pbsSetting = &cssClassReadWrite;
			}

			SetClassProperty(pEl,*pbsSetting,*pbsUnsetting);
			SetClassPropertyOnViewlinkBody(*pbsSetting,*pbsUnsetting);
			if(m_pMIPProxy)
				SetClassProperty(m_pMIPProxy,*pbsSetting,*pbsUnsetting);

			if(m_bReadonly != bReadonly)
			{
				if(bReadonly)
				{
					CSSEffects::ShowEffect(EVENT_READONLY,pEl);
				}
				else
				{
					CSSEffects::ShowEffect(EVENT_READWRITE,pEl);					
				}
			}
		}
	} else {
		hr = S_OK;
	}
	m_bReadonly = bReadonly;
	return hr;
}

HRESULT CXFormsElement::enable(BOOL bEnabled)
{
	HRESULT hr = E_FAIL;
	//Get the correct element to attach the class to.
	// Don't need to add the css class to the element as we would already 
	// have done this by calling IBoundTarget::setInitalState() in CBoundNode::addTarget()
	if(!m_bBindingToTarget) {
		CComQIPtr<IHTMLElement> pEl;
		if(m_pMain)
		{
			pEl = m_pMain;
			//pEl->AddRef();
		}
		else
		{
			GetMasterElement(&pEl);
		}
		if(pEl)
		{
			//get the properties, the right way around.
			const BSTR * pbsSetting = 0;
			const BSTR * pbsUnsetting = 0;

			if(bEnabled)
			{
				pbsSetting = &cssClassEnabled;
				pbsUnsetting = &cssClassDisabled;
			}	
			else
			{
				pbsUnsetting = &cssClassEnabled;
				pbsSetting = &cssClassDisabled;
				CComQIPtr<IHTMLElement2> pEl2 = pEl;
				pEl2->blur();
			}

			SetClassProperty(pEl,*pbsSetting,*pbsUnsetting);
			SetClassPropertyOnViewlinkBody(*pbsSetting,*pbsUnsetting);
			if(m_pMIPProxy)
				SetClassProperty(m_pMIPProxy,*pbsSetting,*pbsUnsetting);

			if(m_bEnabled != bEnabled)
			{
				if(bEnabled)
				{
					CSSEffects::ShowEffect(EVENT_ENABLED,pEl);
				}
				else
				{
					CSSEffects::ShowEffect(EVENT_DISABLED,pEl);					
				}
				if(m_pDisablee)
				{
					m_pDisablee->put_disabled(bEnabled?VARIANT_FALSE:VARIANT_TRUE);
				}
				
				if(!bEnabled)
				{
					//IE leaves the caret behind sometimes.
					ConcealCaretIfWithinSelf();
				}
			}
			

		}
	} else {
		hr = S_OK;
	}
	m_bEnabled = bEnabled;

	return hr;
}

HRESULT CXFormsElement::valid(BOOL bValid)
{
	//Get the correct element to attach the class to.
	HRESULT hr = E_FAIL;

	// Don't need to add the css class to the element as we would already 
	// have done this by calling IBoundTarget::setInitalState() in CBoundNode::addTarget()
	if(!m_bBindingToTarget) {
		CComQIPtr<IHTMLElement> pEl;
		if(m_pMain)
		{
			pEl = m_pMain;
			//pEl->AddRef();
		}
		else
		{
			GetMasterElement(&pEl);
		}
		if(pEl)
		{
			//get the properties, the right way around.
			const BSTR * pbsSetting = 0;
			const BSTR * pbsUnsetting = 0;

			if(bValid)
			{
				pbsSetting = &cssClassValid;
				pbsUnsetting = &cssClassInvalid;
			}	
			else
			{
				pbsUnsetting = &cssClassValid;
				pbsSetting = &cssClassInvalid;
			}
			SetClassProperty(pEl,*pbsSetting,*pbsUnsetting);
			SetClassPropertyOnViewlinkBody(*pbsSetting,*pbsUnsetting);
			if(m_pMIPProxy)
				SetClassProperty(m_pMIPProxy,*pbsSetting,*pbsUnsetting);

			if(m_bValid != bValid)
			{
				if(bValid)
				{
					CSSEffects::ShowEffect(EVENT_VALID,pEl);
					SetClassProperty(pEl,L"",cssBlurredInvalid);
					SetClassPropertyOnViewlinkBody(L"",cssBlurredInvalid);
				}
				else
				{
					CSSEffects::ShowEffect(EVENT_INVALID,pEl);					
					if(m_bVisited)
					{
						if(m_bFocus)
						{
							SetClassProperty(pEl,L"",cssBlurredInvalid);
							SetClassPropertyOnViewlinkBody(L"",cssBlurredInvalid);
						}
						else
						{
							SetClassProperty(pEl,cssBlurredInvalid,L"");
							SetClassPropertyOnViewlinkBody(cssBlurredInvalid,L"");
						}
					}
				}
			}
		}
	} else {
		hr = S_OK;
	}
	m_bValid = bValid;

	return hr;
}


HRESULT CXFormsElement::required(BOOL bRequired)
{
	HRESULT hr = E_FAIL;
	// Don't need to add the css class to the element as we would already 
	// have done this by calling IBoundTarget::setInitalState() in CBoundNode::addTarget()
	if(!m_bBindingToTarget) {
		//get the properties, the right way around.
		const BSTR * pbsSetting = 0;
		const BSTR * pbsUnsetting = 0;

		if(bRequired)
		{
			pbsSetting = &cssClassRequired;
			pbsUnsetting = &cssClassOptional;
		}	
		else
		{
			pbsUnsetting = &cssClassRequired;
			pbsSetting = &cssClassOptional;
		}


		CComQIPtr<IHTMLElement> pEl;
		if(m_pMain)
		{
			pEl = m_pMain;
			//pEl->AddRef();
		}
		else
		{
			GetMasterElement(&pEl);
		}

		if(m_pMIPProxy)
			SetClassProperty(m_pMIPProxy,*pbsSetting,*pbsUnsetting);

		if(pEl)
		{
			SetClassProperty(pEl,*pbsSetting,*pbsUnsetting);
			SetClassPropertyOnViewlinkBody(*pbsSetting,*pbsUnsetting);
			if(m_bRequired != bRequired)
			{
				if(bRequired)
				{
					CSSEffects::ShowEffect(EVENT_REQUIRED,pEl);
				}
				else
				{
					CSSEffects::ShowEffect(EVENT_OPTIONAL,pEl);					
				}
			}
		}
	} else {
		hr = S_OK;
	}
	m_bRequired= bRequired;

	return hr;
}


HRESULT CXFormsElement::showFocus(bool bFocus)
	{
	const BSTR * pbsSetting = 0;
	const BSTR * pbsUnsetting = 0;
	HRESULT hr = S_OK;
	if(bFocus)
	{
		pbsSetting = &cssClassInFocus;
		pbsUnsetting = &cssClassOutOfFocus;
	}	
	else
	{
		pbsUnsetting = &cssClassInFocus;
		pbsSetting = &cssClassOutOfFocus;
	}


	CComQIPtr<IHTMLElement> pEl;
	if(m_pMain)
	{
		pEl = m_pMain;
		//pEl->AddRef();
	}
	else
	{
		hr = GetMasterElement(&pEl);
	}
	if(m_pMIPProxy)
		SetClassProperty(m_pMIPProxy,*pbsSetting,*pbsUnsetting);

	if(pEl)
	{
		SetClassProperty(pEl,*pbsSetting,*pbsUnsetting);
		SetClassPropertyOnViewlinkBody(*pbsSetting,*pbsUnsetting);
		if(m_bFocus != bFocus)
		{
			if(bFocus)
			{
				hr = CSSEffects::ShowEffect(EVENT_DOMFOCUS_IN,pEl);
				m_bVisited = true;
			}
			else
			{
				hr = CSSEffects::ShowEffect(EVENT_DOMFOCUS_OUT,pEl);
				if(!m_bValid)
				{
					SetClassProperty(pEl,cssBlurredInvalid,L"");
					SetClassPropertyOnViewlinkBody(cssBlurredInvalid,L"");
				}
				else
				{
					SetClassProperty(pEl,L"",cssBlurredInvalid);
					SetClassPropertyOnViewlinkBody(L"",cssBlurredInvalid);
				}

			}
		}
	}

	m_bFocus = bFocus;
	return hr;
}

HRESULT CXFormsElement::valueChangedEffect()
{
	CComQIPtr<IHTMLElement> pEl;
	if(m_pMain)
	{
		pEl = m_pMain;
	}
	else
	{
		GetMasterElement(&pEl);
	}
	return CSSEffects::ShowEffect(EVENT_VALUECHANGED,pEl);
}
HRESULT CXFormsElement::OutOfRangeEffect()
{
	CComQIPtr<IHTMLElement> pEl;
	if(m_pMain)
	{
		pEl = m_pMain;
	}
	else
	{
		GetMasterElement(&pEl);
	}
	return CSSEffects::ShowEffect(EVENT_OUTOFRANGE,pEl);
}
HRESULT CXFormsElement::InRangeEffect()
{
	CComQIPtr<IHTMLElement> pEl;
	if(m_pMain)
	{
		pEl = m_pMain;
	}
	else
	{
		GetMasterElement(&pEl);
	}
	return CSSEffects::ShowEffect(EVENT_INRANGE,pEl);
}

const unsigned int msg_selected_effect_phase_1 = WM_APP+10;
const unsigned int msg_selected_effect_phase_2 = WM_APP+11;
const unsigned int msg_deselected_effect_phase_1 = WM_APP+12;
const unsigned int msg_deselected_effect_phase_2 = WM_APP+13;

HRESULT CXFormsElement::SelectedEffect()
{


	CComQIPtr<IHTMLElement> pEl;
	if(m_pMain)
	{
		pEl = m_pMain;
	}
	else
	{
		GetMasterElement(&pEl);
	}
	bool bEffectShown = false;
	SetClassProperty(pEl,cssClassSelected,cssClassDeselected);
	CComBSTR sCSSCode;
	HRESULT hr = CSSEffects::GetEffectCSSCode(EVENT_SELECTED,pEl,sCSSCode);
	if(FAILED(hr))
	{
		hr = S_OK;	
	}
	else
	{
		MSG msg;
		// force msg-q to be created just in case, NOP otherwise
		PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE|PM_NOYIELD);
		// msg to myself to do work
		// msg to myself to do work
		BOOL bPostSuccessful = PostMessage(0, msg_selected_effect_phase_2, 0, 0);
		if(!bPostSuccessful)
		{
			DWORD dwLastError = ::GetLastError();
			::MessageBox(0,"failed to post selected message",0,0);
		}
		else
		{

			// msg-pump
			// while (GetMessage(&msg, NULL, 0, 0))
			while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) 
			{
				if(msg.hwnd == NULL &&  msg.message ==  msg_selected_effect_phase_2)
				{
					hr =  CSSEffects::ExecuteCSSCode(sCSSCode,pEl);
					bEffectShown = (SUCCEEDED(hr));

				}
				else if( msg.hwnd == NULL && msg.message == WM_NULL)
				{
					break;
				}
				else
				{
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			}
		}
	}

	//If the effect did not execute for some reason, normally because there is no styled effect, 
	// but also because viewlink occasionally fucks with the above msg queue.
	//	do the default effect.
	if(!bEffectShown)
	{
		CComPtr<IHTMLStyle> pStyle;
		pEl->get_style(&pStyle);
		if(pStyle)
		{
			CComBSTR s;
			pStyle->get_display(&s);
			if(s == L"none")
				pStyle->put_display(m_sFormerDisplayProperty);
		}
	}

	return S_OK;
}

HRESULT CXFormsElement::DeselectedEffect()
{

	CComQIPtr<IHTMLElement> pEl;
	if(m_pMain)
	{
		pEl = m_pMain;
	}
	else
	{
		GetMasterElement(&pEl);
	}

	bool bEffectShown = false;
	SetClassProperty(pEl,cssClassDeselected,cssClassSelected);
	CComBSTR sCSSCode;
	HRESULT hr = CSSEffects::GetEffectCSSCode(EVENT_DESELECTED,pEl,sCSSCode);
	if(FAILED(hr))
	{
		hr = S_OK;	
	}
	else
	{
		MSG msg;
		// force msg-q to be created just in case, NOP otherwise
		PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE | PM_NOYIELD);
		// msg to myself to do work
		BOOL bPostSuccessful = PostMessage(0,msg_deselected_effect_phase_2, 0, 0);
		if(!bPostSuccessful)
		{
			DWORD dwLastError = ::GetLastError();
			::MessageBox(0,"failed to post deselected message",0,0);
		}
		else
		{
			CComQIPtr<IHTMLElement> pEl;
			if(m_pMain)
			{
				pEl = m_pMain;
			}
			else
			{
				GetMasterElement(&pEl);
			}
			//IE goes apeshit trying to set "display:none" if the current Active Element
			//	is a descendent of the element that it is trying to hide.
			//	Shift focus to the body.
			if(pEl)
			{
				CComPtr<IDispatch> pDocDisp;
				pEl->get_document(&pDocDisp);
				CComQIPtr<IHTMLDocument2> pDoc(pDocDisp);
				if(pDoc)
				{
					//Get the Active Element
					CComPtr<IHTMLElement> pActive;
					pDoc->get_activeElement(&pActive);
					if(pActive)
					{
						//Find out whether it is within the element about to hide
						VARIANT_BOOL b;
						pEl->contains(pActive,&b);
						if(b)
						{
							//Get the body
							CComPtr<IHTMLElement> pBodyEl;
							pDoc->get_body(&pBodyEl);
							//set focus.
							CComQIPtr<IHTMLElement2> pBodyEl2(pBodyEl);
							if(pBodyEl2)
								pBodyEl2->focus();

						}
					}
				}
			}

			// msg-pump
			//    while (GetMessage(&msg, NULL, 0, 0))
			while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) 
			{
				if(msg.hwnd == NULL &&   msg.message == msg_deselected_effect_phase_2)
				{
					hr =  CSSEffects::ExecuteCSSCode(sCSSCode,pEl);
					bEffectShown = (SUCCEEDED(hr));
				}
				else if(msg.hwnd == NULL && msg.message == WM_NULL)
				{
					break;
				}
				else
				{
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			}
		}
	}
	//If the effect did not execute for some reason, normally because there is no styled effect, 
	//	but also because viewlink occasionally fucks with this queue 
	//		(which is why it isn't in the message loop). 
	
	//	do the default effect.
	if(!bEffectShown)
	{
		CComPtr<IHTMLStyle> pStyle;
		pEl->get_style(&pStyle);
		if(pStyle)
		{
			CComQIPtr<IHTMLElement2> pEl2(pEl);
			if(pEl2)
			{
				CComPtr<IHTMLCurrentStyle> pCS;
				pEl2->get_currentStyle(&pCS);
				if(pCS)
				{
					m_sFormerDisplayProperty.Empty();
					pCS->get_display(&m_sFormerDisplayProperty);
				}
				if(!pCS || !m_sFormerDisplayProperty || m_sFormerDisplayProperty == L"none")
				{
					m_sFormerDisplayProperty = L"inline-block";
				}
			}
			const CComBSTR sNone(L"none");
			pStyle->put_display(sNone);
		}
	}
	return S_OK;
}

HRESULT SetClassPropertyOnElement(IHTMLElement * pEl, bool bEnabled, bool bReadOnly, bool bValid, bool bRequired,bool * pbModified) 
{
	HRESULT hr = S_OK;
	CComBSTR sClassNames;
		pEl->get_className(&sClassNames);
		CStringW sNewClassNames(sClassNames);
		bool bModified = false;
		bool bTempModified = false;
		// Set Valid or Invalid css class 
		if(bValid && !(bTempModified = sNewClassNames.Replace(cssClassInvalid, cssClassValid) > 0) && sNewClassNames.Find(cssClassValid) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassValid;
		} else if(!bValid && !(bTempModified = sNewClassNames.Replace(cssClassValid, cssClassInvalid) > 0) && sNewClassNames.Find(cssClassInvalid) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassInvalid;
		}
		if(bTempModified)
			bModified = true;
		// Set ReadOnly or ReadWrite css class
		if(bReadOnly && !(bTempModified = sNewClassNames.Replace(cssClassReadWrite, cssClassReadOnly) > 0) && sNewClassNames.Find(cssClassReadOnly) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassReadOnly;
		} else if( !bReadOnly && !(bTempModified = sNewClassNames.Replace(cssClassReadOnly, cssClassReadWrite) > 0) && sNewClassNames.Find(cssClassReadWrite) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassReadWrite;
		}
		if(bTempModified)
			bModified = true;
		// Set Required or Optional css class
		if(bRequired && !(bTempModified = sNewClassNames.Replace(cssClassOptional, cssClassRequired) > 0) && sNewClassNames.Find(cssClassRequired) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassRequired;
		} else if(!bRequired && !(bTempModified = sNewClassNames.Replace(cssClassRequired, cssClassOptional) > 0) && sNewClassNames.Find(cssClassOptional) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassOptional;
		}
		if(bTempModified)
			bModified = true;
		// Set Endabled or Disabled css class
		if(bEnabled && !(bTempModified = sNewClassNames.Replace(cssClassDisabled, cssClassEnabled) > 0)&& sNewClassNames.Find(cssClassEnabled) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassEnabled;
		} else if(!bEnabled && !(bTempModified = sNewClassNames.Replace(cssClassEnabled, cssClassDisabled) > 0) && sNewClassNames.Find(cssClassDisabled) == -1) {
			bModified = true;
			sNewClassNames += " ";
			sNewClassNames += cssClassDisabled;
		}
		
		if(bTempModified && !bModified)
		{
			*pbModified = true;
			sClassNames.Attach(sNewClassNames.AllocSysString());
			hr = pEl->put_className(sClassNames);
		}
		else
		{
			CComBSTR sOldClassNames;
			hr = pEl->get_className(&sOldClassNames);
			if(sClassNames == L"")
			{
				*pbModified = true;
				sClassNames.Attach(sNewClassNames.AllocSysString());
				hr = pEl->put_className(sClassNames);
			}
		}

		return hr;
}

HRESULT CXFormsElement::SetClassProperty(bool bEnabled, bool bReadOnly, bool bValid, bool bRequired) 
{

	HRESULT hr = S_FALSE;

	CComPtr<IHTMLElement> pEl;
	GetMasterElement(&pEl);
	if(pEl)
	{
		bool bModified;
		if(m_pMIPProxy)
			SetClassPropertyOnElement(m_pMIPProxy, bEnabled,  bReadOnly,  bValid,  bRequired, &bModified);
		SetClassPropertyOnElement(pEl, bEnabled,  bReadOnly,  bValid,  bRequired, &bModified) ;
		
		CComPtr<IHTMLDocument> pVLDoc0;
		GetViewlinkDoc(&pVLDoc0);
		CComQIPtr<IHTMLDocument2> pVLDoc(pVLDoc0);
		if(pVLDoc)
		{
			CComPtr<IHTMLElement> pBody;
			pVLDoc->get_body(&pBody);
			if(pBody)
			{
				SetClassPropertyOnElement(pBody, bEnabled,  bReadOnly,  bValid,  bRequired, &bModified) ;
			}
		}

		if(bModified) {
			m_bUpdatePending = true;
			updateAppearance();
		}
	}

	return hr;
}


HRESULT CXFormsElement::ConcealCaretIfWithinSelf()
{
	//Basically, if appropriate, this function
	//(1)blurs the active element, 
	//(2)sets the switch to be the activeElement
	//(3)sets the body to be the activeElement
	//Unfortunately, each of these stages is needed. 
	//Theoretically, (1), should be enough, except if the next element to gain focus were also within the case
	//	However, it doesn't work on its own (but is still needed)
	//(2), on its own, (or with (1)), does not prevent the floaty  caret problem, and can lead to unsightly effects.
	//(3), on its own (or with (1)), does bugger all.
	HRESULT hr = E_FAIL;
	if(m_pXForms)
	{
		CComPtr<IHTMLDocument2> pDoc;
		hr = m_pXForms->GetOwnerDocument(&pDoc);
		if(pDoc)
		{
			CComPtr<IHTMLElement> pEl;
			hr = pDoc->get_activeElement(&pEl);
			if(pEl)
			{
				bool bContains;
				CComPtr<IHTMLElement> pMaster;
				GetMasterElement(&pMaster);
				bContains = pMaster.IsEqualObject(pEl);
				if(!bContains)
					hr = HTMLContains(pEl,&bContains);
				if(false)
				{
					CComBSTR s;
					pEl->get_outerHTML(&s);
					::MessageBoxW(0,s,0,0);
					pMaster->get_outerHTML(&s);
					::MessageBoxW(0,s,0,0);
				}
				if(bContains)
				{
					CComQIPtr<IHTMLElement2> pEl2(pEl);
					if(pEl2)
					{
						//Blur the active element.
						pEl2->blur();
						if(pMaster)
						{
							CComPtr<IHTMLElement> pSwitchEl;
							hr = pMaster->get_parentElement(&pSwitchEl);
							CComQIPtr<IHTMLElement3> pSwitchEl3(pSwitchEl);
							if(pSwitchEl3)
							{
								hr = pSwitchEl3->setActive();
								xport::ActivateBodyElement(pDoc);
							}
						}
					}
				}
			}
		}
	}
	return hr;
}
